/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
/*
 * Toplogy Viewer
 */

package cgl.narada.topology.viewer;

import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * This class represents all the super clusters consisting of clusters. The
 * super cluster is third level in the node address. If node's address is 
 * 1.2.3.4 then the super cluster id is 2
 */
public class SCluster extends Node {
    public List clusters;
 
    /**
     * Only constructor
     */
    public SCluster(int pSClusterId, int pSSClusterId) {
	// form a node for this cluster. Initialize all the Ids and Position.
	super(pSClusterId, pSSClusterId, INVALID_VALUE, INVALID_VALUE);
	clusters = new ArrayList();
    }

    /**
     * Add a new cluster to the list. Depending upon the total number of 
     * clusters in the list, decide the new positions for each of the cluster. 
     * @param  clusterId id of a cluster
     * @param  nodeId id of a node to be inserted
     * @throws TopologyException if due to some problem cluster is found null
     *         during node insertion
     */
    public void insertCluster(int clusterId, int nodeId) 
	throws TopologyException {
	// Addition of the cluster
	Cluster cluster = getCluster(clusterId);
	if (cluster == null) {
	    cluster = new Cluster(clusterId, this.nodeId, 
				  this.parentClusterId);
	    clusters.add(cluster);
	    repositionClusters();
	}
	// Addition of the node in the cluster
	cluster = getCluster(clusterId);
	if (cluster == null) {
	    throw new TopologyException("cluster not found while insertion");
	} else {
	    cluster.insertNode(nodeId);
	}
    }
    
    /**
     * Deletes an existing cluster inside the super cluster only if that 
     * cluster is empty and then repositions all other clusters. If the cluster
     * does not become empty after the node deletion then just pass the control
     * to the Cluster for deletion of the node. 
     * @param  clusterId id of a cluster containing node to be deleted.
     * @param  nodeId id of a node to be deleted.
     * @param  nodesToBeDeleted a <code>List</code> which will contain node
     *         addresses of the nodes being deleted.
     * @throws TopologyException if no such cluster id found while node 
     *         deletion.
     */
    public void deleteCluster(int clusterId, int nodeId, 
			      List nodesToBeDeleted) 
	throws TopologyException {
	Iterator it = clusters.iterator();
	boolean clusterFound = false;
	while ((it.hasNext()) && (!clusterFound)) {
	    Cluster cluster = (Cluster) it.next();
	    if (cluster.nodeId == clusterId) {
		// found the right cluster
		if (nodeId == INVALID_VALUE) {
		    cluster.deleteAllNodes(nodesToBeDeleted);
		} else {
		    cluster.deleteNode(nodeId, nodesToBeDeleted);
		}
		if (cluster.nodes.isEmpty()) {
		    // cluster becomes empty after node deletion
		    it.remove();
		    repositionClusters();
		}
		clusterFound = true;
	    }
	}
	if (!clusterFound) {
	    throw new TopologyException("error in node deletion at Cluster"
					+ "level");
	}
    }

    /**
     * Deletes all the cluster present in this super cluster.
     * @param nodesToBeDeleted a <code>List</code> which will contain node
     *        addresses of the nodes being deleted.
     */
    public void deleteAllClusters(List nodesToBeDeleted) {
	Iterator it = clusters.iterator();
	while (it.hasNext()) {
	    Cluster c = (Cluster) it.next();
	    c.deleteAllNodes(nodesToBeDeleted);
	    it.remove();
	}
    }

    /**
     * Finds out cluster nearest to the specified coordinates. Then passes
     * control to the cluster for finding a nearest node.
     * @param  x X-coordinate
     * @param  y Y-coordinate
     * @return <code>Node</code> nearest to specified coordinates.
     */
    public Node getNodeAtParticularCoordinates(int x, int y) {
	Cluster c = 
	    (Cluster) PositionCalculation.getNearestNode(clusters, x, y);
	if (c != null) {
	    return c.getNodeAtParticularCoordinates(x, y);
	}
	return null;
    }

    /**
     * Draws the super cluster only if its part is visible on the screen. 
     * Draws all the clusters inside it.
     * @param  g <code>Graphics</code> object for image drawing.
     * @param  limitX1 lower limit for X-coordinate
     * @param  limitY1 lower limit for Y-coordinate
     * @param  limitX2 upper limit for X-coordinate
     * @param  limitY2 upper limit for Y-coordinate
     */
    public void drawSuperCluster(Graphics g, int limitX1, int limitY1,
				 int limitX2, int limitY2) {
	if (this.canFitOnPanel(limitX1, limitY1, limitX2, limitY2)) {
	    this.drawNode(g, limitX1, limitY1, limitX2, limitY2);
	    Iterator itClusters = clusters.iterator();
	    while (itClusters.hasNext()) {
		Cluster c = (Cluster) itClusters.next();
		c.drawCluster(g, limitX1, limitY1, limitX2, limitY2);
	    }
	}
    }

    /**
     * Returns a node in this super cluster with specified cluster and node id.
     * @param  clusterId id of a cluster.
     * @param  nodeId id of a node.
     * @return <code>Node</code> 
     */
    public Node getParticularNode(int clusterId, int nodeId) {
	Cluster cluster = getCluster(clusterId);
	if (cluster != null) {
	    return cluster.getParticularNode(nodeId);
	}
	return null;
    }

    /**
     * Gets the new <code>Position</code> objects and assign then to all the 
     * clusters inside this super cluster. Recurdively calls repositionNodes()
     * for each of the cluster.
     */
    public void repositionClusters() {
	List newPositions = getNewPositions();
	Iterator itPosition = newPositions.iterator();
	Iterator itCluster = clusters.iterator();
	while (itCluster.hasNext()) {
	    Cluster c = (Cluster) itCluster.next();
	    Position p = (Position) itPosition.next();
	    c.nodeLocation = p;
	    c.repositionNodes();
	}
    }
	
    /**
     * Returns particular cluster with specified id
     * @param  clusterId id of a cluster.
     * @return <code>Cluster</code>
     */
    private Cluster getCluster(int clusterId) {
	Iterator it = clusters.iterator();
	while (it.hasNext()) {
	    Cluster c = (Cluster) it.next();
	    if (c.nodeId == clusterId) {
		return c;
	    }
	}
	return null;
    }

    /**
     * Returns a <code>List</code> containing <code>Position</code> objects
     * for all the clusters inside this super cluster.
     * @return <code>List</code> of new Position objects.
     */
    private List getNewPositions() {
	int numberOfNodes = clusters.size();
	return PositionCalculation.getNewPositions(this.nodeLocation,
						   numberOfNodes, 3);	    
    }
};
